#include "game.h"

#define WINDOW_CLASSNAME	TEXT("gklabwindow")
#define WINDOW_TITLE		TEXT("Labirynt")
#define WINDOW_STYLE		(WS_SYSMENU | WS_BORDER | WS_CAPTION)

Menu* Game::menu = NULL;
Level* Game::level = NULL;
Physics* Game::physics = NULL;
Graphics* Game::graphics = NULL;
ResourceManager* Game::resourceManager = NULL;
SoundManager* Game::soundManager = NULL;
InputManager* Game::inputManager = NULL;
HWND Game::hWnd;
Game::GameState Game::gameState = Game::MenuState;
DWORD Game::startTime;

bool Game::initialize()
{
	// czas
	startTime = GetTickCount();

	// okno
	initWindow(800, 600);

	// menedzer wejscia
	inputManager = new InputManager();
	if(!inputManager->initialize(hWnd))
		return false;
	//inputManager->setDevice(InputManager::Joystick1);
	inputManager->setDevice(InputManager::KeyboardWSAD);
	
	// Zasoby
	resourceManager = new ResourceManager();

	// fizyka
	physics = new Physics();

	// grafika
	graphics = new Graphics();
	if(!graphics->initialize())
		return false;
	
	// Dzwiek
	soundManager = new SoundManager();
	if(!soundManager->initialize())
		return false;

	//menu
	menu = new Menu();
	if(!menu->initialize())
		return false;

	// Inicjalizacja pomyslna
	gameState = MenuState;
	return true;
}

void Game::close()
{
	if(level)
		level->release();
	if(menu)
		delete menu;
	if(graphics)
		delete graphics;
	if(soundManager)
		delete soundManager;
	if(physics)
		delete physics;
	if(resourceManager)
		delete resourceManager;
	if(inputManager)
		delete inputManager;
}

void Game::run()
{
    MSG msg;
    while(true)
    {
        if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if(!GetMessage(&msg, NULL, 0, 0))
                return;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

		// rozpocznij renderowanie
		if(MenuState == gameState)
		{
			if(graphics->beginDrawing(true))
			{
				menu->drawMenu();
				graphics->endDrawing();
			}
		}
		else if(GamePlayState == gameState)
		{
			if(graphics->beginDrawing(true))
			{
				graphics->beginScene();

				level->render();
				
				graphics->endScene();
				graphics->endDrawing();
			}
		}
		Sleep(1);
    }
}



LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	switch(Msg)
	{
	case WM_KEYDOWN:
		if(Game::MenuState == Game::getGameState())
		{
			Game::getMenu()->onKeyDown((BYTE)wParam);
		}
		else if(Game::GamePlayState == Game::getGameState())
		{
			// Escape - przejdz do menu
			if(wParam == VK_ESCAPE)
			{
				Game::setGameState(Game::MenuState);
				Game::unloadLevel();
			}

			// Tab - zmiana trybu kamery
			else if(wParam == VK_TAB)
			{
				Camera::Mode mode = Game::getGraphics()->getCamera()->getMode();
				switch(mode)
				{
				case Camera::Normal:
					mode = Camera::SkyCamera;
					break;
				case Camera::SkyCamera:
					mode = Camera::CloseUp;
					break;
				case Camera::CloseUp:
					mode = Camera::Normal;
					break;
				}
				Game::getGraphics()->getCamera()->switchToMode(mode,
					Game::getGraphics()->getCamera()->getDirection());
			}

			// Q - zmiana kierunku kamery
			else if(wParam == 'Q')
			{
				Camera::Direction direction = Game::getGraphics()->getCamera()->getDirection();
				switch(direction)
				{
				case Camera::North:
					direction = Camera::West;
					break;
				case Camera::East:
					direction = Camera::North;
					break;
				case Camera::South:
					direction = Camera::East;
					break;
				case Camera::West:
					direction = Camera::South;
					break;
				}
				Game::getGraphics()->getCamera()->switchToMode(
					Game::getGraphics()->getCamera()->getMode(),
					direction);
			}

			// E - zmiana kierunku kamery
			else if(wParam == 'E')
			{
				Camera::Direction direction = Game::getGraphics()->getCamera()->getDirection();
				switch(direction)
				{
				case Camera::North:
					direction = Camera::East;
					break;
				case Camera::East:
					direction = Camera::South;
					break;
				case Camera::South:
					direction = Camera::West;
					break;
				case Camera::West:
					direction = Camera::North;
					break;
				}
				Game::getGraphics()->getCamera()->switchToMode(
					Game::getGraphics()->getCamera()->getMode(),
					direction);
			}

		}
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}	

	if(Game::getInputManager())
		Game::getInputManager()->processInput(Msg, wParam, lParam);

	return DefWindowProc(hWnd, Msg, wParam, lParam);
}



void Game::initWindow(int width, int height)
{
	WNDCLASSEX wc;
	wc.cbSize			= sizeof(WNDCLASSEX);
	wc.style			= CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc		= (WNDPROC)WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= GetModuleHandle(NULL);
	wc.hIcon			= NULL;
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= WINDOW_CLASSNAME;
	wc.hIconSm			= NULL;
	RegisterClassEx(&wc);

	RECT rc = {0, 0, width, height};
	AdjustWindowRect(&rc, WINDOW_STYLE, FALSE);

	hWnd = CreateWindowEx(0, WINDOW_CLASSNAME, WINDOW_TITLE, WINDOW_STYLE,
		20, 20, rc.right - rc.left, rc.bottom - rc.top,
		GetDesktopWindow(), NULL, wc.hInstance, NULL);
	ShowWindow(hWnd , SW_SHOW);
	SetForegroundWindow(hWnd);
	UpdateWindow(hWnd);
}

#if _DEBUG
void listSceneNodes(int indent, const SceneNode* node)
{
	for(int i = 0; i < indent; i++)
		OutputDebugString(" ");
	OutputDebugString(node->getName().c_str());
	char buf[3] = {'0', '\n', 0};
	buf[0] = '0' + node->getFlag();
	OutputDebugString(buf);

	const std::list<SceneNode*> &subNodes = node->getSubNodes();
	std::list<SceneNode*>::const_iterator it;
	for(it = subNodes.begin(); it != subNodes.end() ; it++)
		listSceneNodes(indent + 1, *it);
}
#endif

/**
 * Laduje poziom
 */
bool Game::loadLevel(const std::string &levelName)
{
	if(level)
		level->release();
	level = (Level*)resourceManager->addResource(levelName, Resource::LevelResource);
	if(!level)
		return false;

#if _DEBUG
	OutputDebugString("Lista wezlow sceny:\n");
	listSceneNodes(0, level->getScene()->getRootNode());
#endif

	return true;
}

void Game::unloadLevel()
{
	if(level)
		level->release();
	level = NULL;
}

/**
 * Rozpoczyna nowa gre
 */
bool Game::startNewGame()
{
	return loadLevel("levels/3-Space v1.4.dat");
}

/**
 * Czas gry (w sekundach)
 */
float Game::getTime()
{
	if((GetTickCount() - startTime) > 3600000)
		startTime += 3600000;
	return (GetTickCount() - startTime) * 0.001f;
}
